// MemLatency.cpp : Defines the entry point for the console application.
//
#define _WIN32_WINNT 0x0601

#include "stdafx.h"
#include "../rdtsc.h"
#include <malloc.h>
#include <stdio.h>
#include <windows.h>
#include <time.h>
#include <vector>

//#define ITER 1000000

int foo(int a)
{
	return a+43;
}

void stride_list(int* list, int size, int stride)
{
	int mid = size/2;
	int c = 0;
	for(int i=0, j=mid; i < size; i+=stride, j+=stride)
	{
		j = j%size;
		list[i] = (int)(list + j);
		list[j] = (int)(list + (i+stride)%size);
	}
}

void randomize_list(int* list, int size, int stride)
{
	std::vector<int*> v;
	for(int i=1; i < size; ++i)
		v.push_back(list+i);
	
	srand ( (unsigned int) time(NULL) );
	int* p = list;
	while(v.size() > 0)
	{
		int s = v.size();
		int index = rand() % v.size();
		
		while(abs((int)(v[index] - p) ) < stride )
		{
			int* vv = v[index];
			index = rand() % v.size();
		}
		int* a = v[index];
		*p = (int)a;
		v.erase(v.begin() + index);
		p = a;
	}
	*p = (int)list;
}

int _tmain(int argc, _TCHAR* argv[])
{ 
	// get arguments
	if( argc != 4)
		return 0;
	
	int size = _ttoi(argv[1]);
	int stride = _ttoi(argv[2]);
    int iter = _ttoi(argv[3]);



	// set the process affinity for only the processor on which it is running, and set it to high priority
	BOOL r;	
	DWORD affinity = 0;
	DWORD curProcessor = 0;
	curProcessor = GetCurrentProcessorNumber();
	affinity = 1 << curProcessor;
	r = SetProcessAffinityMask(GetCurrentProcess(), affinity );
	r = SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);

	// init circular array list
	int* plist = (int*)_aligned_malloc(size, 64);
	int plist_size = size/sizeof(int);
	//randomize_list(plist, plist_size, stride);
	stride_list(plist, plist_size, stride);

	// this does not work - different array size leads to same result
	//for(int i=0; i < plist_size; ++i)
	//{
	//	plist[i] = (int)(plist + (((i+1)*stride) % plist_size));
	//}

	int* p = plist;
	int v;
	// warm up cache
	for(int i=0; i < size*2; ++i)
		v = plist[i%plist_size];
	// for outsmarting optimization so the warm-up loop actually runs
	FILE* f;
	fopen_s(&f, "temp", "w");
	fwrite(&v, sizeof(int), 1, f);

	// start measurement
	__int64 t1 = rdtsc();
	for(int i=0; i < iter; ++i)
	{
		p = (int*)*p;
		//foo(p);
	}
	__int64 t2 = rdtsc();	
	// end measurement, actual latency = cycles - for_loop_cycles - rdtsc_cycles
	
	// this is for optimization on /Og, so the compiler wouldn't optimize out the for loop
	fwrite(&p, sizeof(int*), 1, f);
	fclose(f);

	printf("iteration: %i array size: %10i stride: %i cycles: %10I64d\n", iter, size, stride, t2 - t1);

	_aligned_free(plist);
	return 0;
}

